package net.finnigin.process;

import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class InputStreamGobbler {
	private final static ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory();
	protected final static ExecutorService gobblerThreadService = new ThreadPoolExecutor(
			0,
			Integer.MAX_VALUE,
			30L,
			TimeUnit.MINUTES,
			new SynchronousQueue<Runnable>(),
			new ThreadFactory() {
				@Override
				public Thread newThread(Runnable r) {
					Thread t = defaultThreadFactory.newThread(r);
					t.setDaemon(true);
					return t;
				}
			});
	protected Runnable gobbler;
	protected InputStream inputStream;
	protected Exception exception;
	private boolean started;
	private boolean stopped;

	public InputStreamGobbler() {
		gobbler = initRunnable();
		started = false;
		stopped = false;
		inputStream = null;
		exception = null;
	}

	public InputStreamGobbler(InputStream inputStream, boolean startGobbling) {
		this();
		this.inputStream = inputStream;
		started = startGobbling;
		if (started)
			gobblerThreadService.execute(gobbler = initRunnable());
	}

	private Runnable initRunnable() {
		return new Runnable() {
			public void run() {
				try {
					int thebyte;
					while (!stopped && (thebyte = inputStream.read()) != -1) {
						processByte(thebyte);
					}
				} catch (Exception ex) {
					exception = ex;
				}				
			}
		};
	}

	protected void processByte(int thebyte) {
		// Hook for subclasses
	}

	public void startGobbling() {
		if (started) return;
		started = true;
		gobblerThreadService.execute(gobbler);
	}
	
	public void stopGobbling() {
		stopped = true;
	}
	
	public void setInputStream(InputStream inputStream) {
		if (!started)
			this.inputStream = inputStream;
		else
			throw new IllegalStateException("Gobbler already gobbling.");
	}

	public InputStream getInputStream() {
		return inputStream;
	}

	public Exception getException() {
		return exception;
	}
}
